1 | _ |
reduce是stream的收集器的一种,对于我来说理解起来比较难,我抽出一天半时间来理解.
找到了三个对于理解reduce帮助很大的文章:
- reduce的重载秘密
- 关于Java Lambda表达式看这一篇就够了 主要看reduce部分
- java8中3个参数的reduce方法怎么理解? 这个对于理解很重要
reduce有三个重载方法: 一个参数,二个参数,三个参数
三个参数reduce:
首先理解方法本身的意思:
Stream的reduce方法,翻译过来是聚合或者是汇聚成一个的意思,
由于Stream本身就代表着一堆数据,那stream.reduce()方法顾名思义就是把一堆数据聚合成一个数据.
理解了reduce方法的意思,再来看看这个方法挂靠的对象是stream,是一个流,了解一下流的工作方式:
流底层核心其实是Spliterator接口的一个实现,而这个Spliterator接口其实本身就是Fork/Join并行框架的一个实现,
所以归根结底要明白流的工作方式,就要明白一下Fork/Join框架的基本思想,即:
以递归的方式将可以并行的任务拆分成更小的子任务,然后将每个子任务的结果合并起来生成整体的最后结果
理解了方法本身的意思以及流的工作方式,再结合到一起理解一下stream.reduce()方法,
即用Fork/Join的方式把一堆数据聚合成一个数据,因此可以画出reduce方法的运行草图
结合草图,要实现stream.reduce()方法,必须要告诉JDK:
你有什么需求数据要汇聚?(Stream已经提供了数据源,对应上面草图的A元素)
最后要汇聚成怎样的一个数据类型, 和提供初始值(对应reduce方法的参数一,对应上面草图的B元素)
如何将需求数据处理或转化成一个汇聚数据(对应reduce方法的参数二,对应上面草图的汇聚方式1, 累加器)
如何将多个汇聚数据进行合并(对应reduce方法的参数三,对应上面草图的汇聚方式2, 合路器)
两个参数和三个参数的区别:
reduce(初始值,累加器,合路器) //合路器只在多线程的时候使用.
那么第三个参数有什么用呢?第三个参数是两个相同的类型计算出一个相同的类型,而且都是最终返回类型。
第三个参数只有在并行计算即.stream().parallel()之后才会发生作用。
回想一下,如果不采用并行计算,reduce的计算方式肯定是单线程的,因为不可能直接计算出最终结果,而是每一个计算都依赖于上一个计算。
而并行计算的话,很显然与非并行计算的计算方式不同,如果还是按照原来reduce的方式进行计算,多线程也没有用,因为结果一步一步都互相依赖。
实际上,并行计算之后,计算方式不再是将结果依次与下一个元素计算,而是直接拿初始值与流中的每一个元素进行计算,这个过程被第二个参数控制。
用一个简单的例子来说明:1
2
3
4
5
6
7
8
9
10Stream<Integer> integerStream = Stream.of(3, 4, 5, 6, 7);
Stream<Integer> integerStream2 = Stream.of(3, 4, 5, 6, 7);
int result = integerStream2.reduce(10, (x, y) -> x + y);
int parellelResult = integerStream.parallel().reduce(10, (x, y) -> x + y, (x, y) -> (x * y));
System.out.println("单线程操作的结果是:"+result);
System.out.println("并行操作的结果是:"+parellelResult);
单线程操作的结果很明显,是10+3+4+5+6+7=35。
并行操作呢,结果实际上是(10+3)*(10+4)(10+5)*(10+6)*(10+7),
第二个参数中的(x,y)实际上变成了初始值和每一个元素的运算过程,最后一个参数则表示将所有的结果相乘。
三个重载方法,有四种应用场合,两种计算逻辑。应用reduce的情况可以总结如下
- 不需要初始值且无需改变返回类型,使用单参数的reduce方法,注意返回的是Optional对象
- 需要初始值且无需改变返回类型,使用两参数的reduce方法,直接返回指定类型
- 单线程对所有元素依次操作,需要返回其他类型,使用三参数的reduce方法,第三个参数随便传个lambda表达式即可,没有影响
- 多线程同时操作所有元素然后进行联合,需要增加.parallel(),此时reduce的计算逻辑会发生变化。